import Path from 'path';
import { fileURLToPath } from 'url';
import { createServer, createConnection } from 'net';
import { MessageObserver } from './protocol.js';
import { mkdir, remove } from './fs.js';

const __dirname = Path.dirname(fileURLToPath(import.meta.url));
const sockPath = Path.resolve(__dirname, './tmp/ipc.sock');

export class ServerSide {
    constructor(path = sockPath) {
        this._path = path;
        this._subscriber = new Map();
    }

    subscribe(type, fn) {
        this._subscriber.set(type, fn);
    }

    async startup() {
        await remove(this._path);
        await mkdir(Path.resolve(this._path, './../'));
        this._server = createServer(client => {
            const observer = MessageObserver.form({ readable: client, writable: client });
            observer.isNotify = (type) => {
                return this._subscriber.has(type);
            };
            observer.notify = (type, params) => {
                if (type === 'initialize') {
                    client.pid = request.params.pid;
                } else {
                    return this._subscriber.get(type)(params, client);
                }
            }
        });
        return new Promise((resolve, reject) => {
            this._server.once('error', e => reject(e));
            this._server.listen(this._path, () => {
                console.log('listen:', this._path);
                resolve();
            });
        });
    }
}

export class ClientSide {
    constructor(path = sockPath) {
        this._path = path;
        this._subscriber = new Map();
    }

    async connect() {
        return new Promise((resolve, reject) => {
            const client = createConnection(this._path);
            const observer = this._observer = MessageObserver.form({ readable: client, writable: client });
            observer.isNotify = type => {
                return this._subscriber.has(type);
            }
            observer.notify = (type, params) => {
                return this._subscriber.get(type)(params);
            }
            client.on('connect', () => {
                this._observer.publish('initailize', { pid: process.pid }).then(() => resolve());
            });
            client.once('error', e => reject(e));
            client.on('error', e => console.log(e));
        });
    }

    subscribe(type, fn) {
        this._subscriber.set(type, fn);
    }

    publish(type, params) {
        this._observer.publish(type, params);
    }
}